package asteroids.entity.weapon;

import java.awt.Graphics;
import java.util.ArrayList;

import org.apache.log4j.Logger;

import asteroids.engine.GameState;
import asteroids.engine.Utils;
import asteroids.engine.Utils.PolarCoordinate;
import asteroids.engine.ai.Pilot;
import asteroids.entity.GameObject;
import asteroids.entity.PlayerShip;
import asteroids.entity.Ship;
import asteroids.entity.equipment.Equipment;

public class ConfusionRay extends Weapon
{
    private static Logger logger = Logger.getLogger(ConfusionRay.class);

    private static int refireDelay = (int) ((20d * Utils.FRAMES_PER_SECOND) / 24d);
    private static double energyCost = 20;

    private static ArrayList<Ship> confusedShips;

    // private final Utils.PolarCoordinate[] BOLT_SHAPE = {
    // Utils.createPolarCoordinate(0, 10),
    // Utils.createPolarCoordinate(45, 5), Utils.createPolarCoordinate(90, 10),
    // Utils.createPolarCoordinate(135, 5), Utils.createPolarCoordinate(180,
    // 10),
    // Utils.createPolarCoordinate(225, 5), Utils.createPolarCoordinate(270,
    // 10),
    // Utils.createPolarCoordinate(315, 5), };

    public ConfusionRay(Ship ship)
    {
        super(ship, energyCost, refireDelay);

        logger.trace("Armed " + ship + " with ConfusionRay");
        confusedShips = new ArrayList<Ship>();
    }

    @Override
    public boolean FIRE(GameState state)
    {
        boolean fired = false;
        if( canFireYet(state) )
        {
            manageFireControl(state);
            fired = true;
            Utils.CartesianCoordinate firePoint = Utils.cartesianFromPolar(
                    ((PlayerShip) ship).getHardPoints()[0].rotateBy(ship.getRotation())).shiftBy(
                    ship.getXPosition(), ship.getYPosition());

            Bullet cBolt = new ConfusionBolt(firePoint.getXComponent(), firePoint.getYComponent(),
                    ship.getRotation(), 20, 10, (int) (Utils.FRAMES_PER_SECOND / 2d),
                    Utils.createPolarCircle(10), ship);
            ship.add(cBolt);
        }

        return fired;
    }

    public static boolean hasConfusedShip(Ship s)
    {
        return confusedShips.contains(s);
    }

    public static void addConfusedShip(Ship s)
    {
        confusedShips.add(s);
    }

    public static boolean removeConfusedShip(Ship s)
    {
        return confusedShips.remove(s);
    }
}

class ConfusionBolt extends Bullet
{
    private static Logger logger = Logger.getLogger(ConfusionBolt.class);

    public ConfusionBolt(double xpos, double ypos, double direction, double speed, double power,
            int timeToLive, PolarCoordinate[] shape, Ship ship)
    {
        super(xpos, ypos, direction, speed, power, timeToLive, shape, ship);
    }

    @Override
    public void collide(GameObject other, double power)
    {
        if( other.getType() != GameObject.ObjectType.SHIP )
        {
            super.collide(other, power);
        }
        else
        {
            if( !other.getAffiliation().equals(affiliation) )
            {
                Ship s = (Ship) other;
                logger.trace("ConfusionBolt hit " + s);
                ConfusionRegulator cr = ConfusionRegulator.createRegulator(s);
                if( cr != null ) // if the ship is not already confused
                {
                    logger.trace("confusing " + s);
                    // confuse the ship!
                    s.equip(cr);
                }
                destroy();
            }
        }
    }

    @Override
    public double getCollisionPower(GameObject other)
    {
        return 0d;
    }

    public void draw(Graphics g)
    {
        super.draw(g);
        g.drawOval((int) (x_position - 4), (int) (y_position - 4), 8, 8);
    }
}

/**
 * this is added to the target ship to undo confusion and draw confusion effects
 * 
 * @author whitecd1
 * 
 */
class ConfusionRegulator extends Equipment
{
    public static Logger logger = Logger.getLogger(ConfusionRegulator.class);

    private int CONFUSION_LIMIT = Utils.FRAMES_PER_SECOND * 5;
    private int confusionTime = 0;

    private ConfusedPilot confusedPilot;

    private ConfusionRegulator(Ship ship)
    {
        super(ship);

        confusedPilot = new ConfusedPilot(ship);
        ship.setPilot(confusedPilot);
        ConfusionRay.addConfusedShip(ship);
    }

    public static ConfusionRegulator createRegulator(Ship ship)
    {
        ConfusionRegulator cr = null;
        if( !ConfusionRay.hasConfusedShip(ship) )
        {
            cr = new ConfusionRegulator(ship);
        }
        return cr;
    }

    @Override
    public void update(GameState state)
    {
        super.update(state);
        if( confusionTime >= CONFUSION_LIMIT )
        {
            destroy();
        }
        confusionTime++;
    }

    /**
     * if a pilot is confused when respawning, un-confuse him
     */
    @Override
    public void respawn()
    {
        destroy();
    }

    public void destroy()
    {
        if( confusedPilot != null )
        {
            confusedPilot.unFaze();
            ConfusionRay.removeConfusedShip(ship);
        }
        confusedPilot = null;
        super.destroy();
    }
}

/**
 * Decorates the pilot so he can do nothing but shoot.
 * 
 * @author whitecd1
 * 
 */
class ConfusedPilot implements Pilot
{
    private Pilot cPilot;
    private Ship ship;

    public ConfusedPilot(Ship s)
    {
        cPilot = s.getPilot();
        ship = s;
        s.setPilot(this);
    }

    @Override
    public int getOrders(GameState state)
    {
        int orders = cPilot.getOrders(state);
        // annul all orders except firing
        orders = orders & Pilot.FIRE;
        // turn right. Always.
        orders = orders | Pilot.RIGHT;

        return orders;
    }

    public void unFaze()
    {
        ship.setPilot(cPilot);
    }
}